package com.lwp.excel.resolver.impl;

import com.lwp.excel.annotation.Cell;
import com.lwp.excel.annotation.Font;
import com.lwp.excel.annotation.Style;
import com.lwp.excel.exception.NotFoundCellRunTimeException;
import com.lwp.excel.resolver.ExcelAble;
import com.lwp.excel.resolver.ExcelResolver;
import com.lwp.excel.util.ExcelUtil;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.RegionUtil;

import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;

/**
 * Copyright (C) @2019 GuangDong Eshore Technology Co. Ltd
 *
 * @author: Administrator
 * @version: 1.0
 * @date: 2019/8/9
 * @time: 15:46
 * @description: 系统默认的Excel解析器。
 * 解析
 * 主要功能负责解析解析Excel的行和列。
 * 数据结构：可能是一对一，可能一对多：
 * 一对一： 列中有表头
 * 一对多： 列中有表
 * 和对应数据条数的对应关系的解析。和行数和列数的占用解析。
 */
public abstract class DefaultExcelResolver implements ExcelResolver {


    public Class<?> group;

    public DefaultExcelResolver(Class<?> group) {
        this.group = group;
    }

    private List<HSSFCellStyle> dataStyles = new ArrayList<>();

    private HSSFCellStyle defaultStyle = null;

    private List<HSSFFont> dataFonts = new ArrayList<>();

    private HSSFFont defaultFont = null;

    public DefaultExcelResolver() {
    }

    /**
     * 获取最后需要创建行的索引
     *
     * @param sheet
     * @return
     */
    @Override
    public int lastRowIndex(HSSFSheet sheet) {
        return sheet.getLastRowNum() + 1;
    }

    /**
     * 获取最后需要创建列的索引
     *
     * @param row
     * @return
     */
    @Override
    public int lastCellIndex(HSSFRow row) {
        int lastIndex = row.getLastCellNum();
        return lastIndex==-1?0:lastIndex;
    }


    @Override
    public int countParticleRow(Class<?> clazz,String[] headers) {
        HeaderExcelResolver headerExcelResolver = new HeaderExcelResolver(group, headers,null);
        headerExcelResolver.initTempHeaderMap(null);
        return countParticleRow(clazz, headers, headerExcelResolver);
    }

    /**
     * 获取数据最大粒子行数：
     * 传入一条数据，
     * 返回一个装载该数据表头需要的行数。
     *
     * @param clazz
     * @return 传入Class返回一共需要多少行能够展示该表头。
     */
    public int countParticleRow(Class<?> clazz,String[] headers,HeaderExcelResolver headerExcelResolver) {
        //使用反射，获取
        Field[] fields = clazz.getDeclaredFields();
        int countRow = 0;
        boolean hasCell = false;
        Cell cell = null;
        for (Field field :
                fields) {
            int itemCountRow = 0;
            cell = field.getAnnotation(Cell.class);
            if (cell != null &&headerExcelResolver.verifyField(field, headerExcelResolver)) {//如果属性被@Cell注解标注
                hasCell = true;
                Class<?> genericType = null;
                if (ExcelAble.class.isAssignableFrom(field.getType())) {//字段类型是ExcelVO
                    genericType = field.getType();
                } else if (Collection.class.isAssignableFrom(field.getType())) {//字段类型是数据列表。
                    Type type = field.getGenericType();
                    if (type == null) continue;
                    //得到泛型类型的类名
                    if (type instanceof ParameterizedType) {
                        ParameterizedType parameterizedType = (ParameterizedType) type;
                        //得到泛型里的class类型对象
                        genericType = (Class<?>) parameterizedType.getActualTypeArguments()[0];
                    }
                }
                if (genericType != null) {
                    //headerExcelResolver.initTempHeaderMap(field.getName());
                    HeaderExcelResolver itemResolver = new HeaderExcelResolver(group,headers,null);
                    itemResolver.setTempHeaderMap((HashMap<String,Object>)headerExcelResolver.getTempHeaderMap().get(field.getName()));
                    itemCountRow += countParticleRow(genericType,headers,itemResolver);
                }
                if (itemCountRow >= countRow) {//保证countRow是最大的。
                    countRow = itemCountRow;
                }
            }
        }
        if (!hasCell) {//遍历结束后，没有任何一个属性被@Cell 标注。
            throw new NotFoundCellRunTimeException(clazz.getName() + " is not found the Cell annotation");
        }
        countRow++;
        return countRow;
    }

    @Override
    public int countParticleCell(Class<?> clazz, String[] headers) {//
        HeaderExcelResolver header = new HeaderExcelResolver(group,headers,null);
        header.initTempHeaderMap(null);
        return countParticleCell(clazz,headers,header);
    }


    /**
     * 传入一个数据Class，
     * 返回一个装在该数据需要的列数。
     *
     * @param clazz 字段类型/字段如果是集合，则是泛型的类型。
     * @return
     */
    public int countParticleCell(Class<?> clazz,String[] headers,HeaderExcelResolver header) {//
        Field[] fields = clazz.getDeclaredFields();
        //字段需要占用Excel的列数。
        int cellCount = 0;
        //验证
        //是否有字段
        if (fields==null||fields.length==0) {
            return 0;
        }

        //被@Cell注解标注↓↓↓↓↓
        if (ExcelAble.class.isAssignableFrom(clazz)) {//是多属性字段（字段是实例对象）
            for (Field field :
                    fields) {
                //1.验证字段是否被@Cell注解标注
                Cell cell = field.getAnnotation(Cell.class);
                if (cell == null) {//没有被@Cell注解标注
                    continue;
                }

                if (!header.verifyField(field, header)) {
                    continue;
                }
                Class<?> genericType = null;
                if (ExcelAble.class.isAssignableFrom(field.getType())) {
                    genericType = field.getType();
                } else if (Collection.class.isAssignableFrom(field.getType())) {
                    Type type = field.getGenericType();

                    if (type == null) continue;
                    //得到泛型类型的类名
                    if (type instanceof ParameterizedType) {
                        ParameterizedType parameterizedType = (ParameterizedType) type;
                        //得到泛型里的class类型对象
                        genericType = (Class<?>) parameterizedType.getActualTypeArguments()[0];
                    }
                } else {
                    cellCount ++;
                }
                if (genericType != null) {
                    HeaderExcelResolver itemResolver = new HeaderExcelResolver(group,headers,null);
                    itemResolver.setTempHeaderMap((HashMap<String,Object>)header.getTempHeaderMap().get(field.getName()));
                    cellCount += countParticleCell(genericType,headers,itemResolver);
                }

            }
        } else {
            cellCount = 1;
        }
        return cellCount;
    }


    @Override
    public int countParticleValRow(Object data,String[] headers) {
        HeaderExcelResolver header = new HeaderExcelResolver(group,headers,null);
        header.initTempHeaderMap(null);
        return countParticleValRow(data, headers, header);
    }

    /**
     * 传入一个数据 ，
     * 获取该数据需要多少行来装载该数据
     * 分两种情况：
     * 1.字段数据类型为ExcelAble 或其他基本类型及包装类型。这样装载每条数据需要1行。
     * 2.字段是集合类型，那么是按照集合列表及子集合列表的数据条数和。
     *
     * @param data
     * @return
     */
    public int countParticleValRow(Object data,String[] headers,HeaderExcelResolver header) {
        Class<?> clazz = data.getClass();
        int valueCount = 0;
        //获取所有字段
        Field[] fields = clazz.getDeclaredFields();
        for (Field field :
                fields) {
            //1.验证字段是否被@Cell注解标注
            Cell cell = field.getAnnotation(Cell.class);
            if (cell == null) {//没有被@Cell注解标注
                continue;
            }

            if (!header.verifyField(field, header)) {
                continue;
            }
            int fieldValueCount = 0;
            if (Collection.class.isAssignableFrom(field.getType())) {//数据是数据列表
                Collection value = null;
                try {
                    Method method = clazz.getMethod("get" + ExcelUtil.getMethodName(field.getName()));
                    value = (Collection) method.invoke(data);
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
                if (value != null) {
                    for (Object itemData :
                            value) {
                        HeaderExcelResolver itemHeader = new HeaderExcelResolver(group,headers,null);
                        itemHeader.setTempHeaderMap(header.getTempHeaderMap());
                        fieldValueCount += countParticleValRow(itemData,headers,itemHeader);
                    }
                } else {
                    fieldValueCount = 0;
                }
            } else {//不是列表，不是列表只返回一条即可
                fieldValueCount = 1;
            }
            if (fieldValueCount > valueCount) { //一直保证valueCount的值，是最大的，这样就能返回最大数据行数了。
                valueCount = fieldValueCount;
            }
        }
        return valueCount;
    }

    public void setRangeBorder(short i, CellRangeAddress cellRangeAddress, HSSFSheet sheet , Workbook wb){
        RegionUtil.setBorderBottom(i, cellRangeAddress, sheet,wb); // 下边框
        RegionUtil.setBorderLeft(i, cellRangeAddress, sheet,wb); // 左边框
        RegionUtil.setBorderRight(i, cellRangeAddress, sheet,wb); // 有边框
        RegionUtil.setBorderTop(i, cellRangeAddress, sheet,wb); // 上边框
    }

    public void setRangeBorderColor(short i, CellRangeAddress cellRangeAddress, HSSFSheet sheet , Workbook wb){
        RegionUtil.setBottomBorderColor(i, cellRangeAddress, sheet,wb); // 下边框
        RegionUtil.setLeftBorderColor(i, cellRangeAddress, sheet,wb); // 左边框
        RegionUtil.setRightBorderColor(i, cellRangeAddress, sheet,wb); // 有边框
        RegionUtil.setTopBorderColor(i, cellRangeAddress, sheet,wb); // 上边框
    }


    public HSSFCellStyle getStyle(HSSFWorkbook wb, Style style, Font font) {
        HSSFCellStyle resStyle = null;
        //遍历
        if (style == null && font == null) {
            if (defaultStyle == null) {
                return createStyle(wb, style, font);
            } else {
                return defaultStyle;
            }
        }
        for (int i = 0; i < dataStyles.size(); i++) {
            HSSFCellStyle cellStyle = dataStyles.get(i);
            boolean isSame = false;
            if (style == null && font == null) {
                if (defaultStyle == null) {
                    return createStyle(wb, style, font);
                } else {
                    return defaultStyle;
                }
            }
            if (style != null) {
                isSame = style.halign() == cellStyle.getAlignment() &&
                        style.valign() == cellStyle.getVerticalAlignment() &&
                        style.border() == cellStyle.getBorderTop() &&
                        style.color() == cellStyle.getTopBorderColor() &&
                        style.fillPattern() == cellStyle.getFillPattern() &&
                        style.backgroundColor() == cellStyle.getFillForegroundColor();
                if (font != null) {
                    HSSFFont fontStyle = cellStyle.getFont(wb);
                    isSame = isSame && font.fontColor() == fontStyle.getColor()
                            && font.fontHeightInPoints()==fontStyle.getFontHeightInPoints()
                            && font.fontName() == fontStyle.getFontName();
                }
            } else {
                if (font != null) {
                    HSSFFont fontStyle = cellStyle.getFont(wb);
                    isSame = font.fontColor() == fontStyle.getColor()
                            && font.fontHeightInPoints()==fontStyle.getFontHeightInPoints()
                            && font.fontName() == fontStyle.getFontName();
                }
            }
            if (isSame) {
                return cellStyle;
            }
        }
        return createStyle(wb, style, font);
    }

    public HSSFFont getFont(HSSFWorkbook wb,Font font,HSSFCellStyle cellStyle) {
        boolean isSame = false;
        HSSFFont resFont = null;

        for (int i = 0; i < dataFonts.size(); i++) {
            HSSFFont hssfFont = dataFonts.get(i);
            if (font == null) {//默认字体。
                if (defaultFont == null) {
                    defaultFont = createFont(font, wb);//创建默认字体。
                }
                return defaultFont;
            } else {//不是默认字体
                isSame = font.fontColor() == hssfFont.getColor()
                        && font.fontHeightInPoints()==hssfFont.getFontHeightInPoints()
                        && font.fontName() == hssfFont.getFontName();
            }
            if (isSame) {
                return hssfFont;
            }

        }
        resFont = createFont(font, wb);
        dataFonts.add(resFont);
        return resFont;

    }

    public HSSFCellStyle createStyle(HSSFWorkbook wb, Style style, Font font) {
        HSSFCellStyle cellStyle = null;
        if (style != null) {
            cellStyle = wb.createCellStyle();
            //设置水平对齐样式
            cellStyle = halign(wb, style, cellStyle);
            //设置垂直对齐样式
            cellStyle = valign(wb, style, cellStyle);
            //设置边框及颜色
            cellStyle = border(wb, style, cellStyle);
            //设置填充样式和背景颜色
            cellStyle = fillPattern(wb, style, cellStyle);
        }
        //设置字体
        HSSFFont fontStyle = getFont(wb,font,cellStyle);
        if (fontStyle != null) {
            if (cellStyle == null) {
                cellStyle = wb.createCellStyle();
            }
            cellStyle.setFont(fontStyle);
        }
        if (cellStyle!=null) {
            if (style == null && font == null) {//默认style
                defaultStyle = cellStyle;
            } else {
                dataStyles.add(cellStyle);
            }
        }
        return cellStyle;
    }

    public HSSFFont createFont(Font font,HSSFWorkbook wb){
        HSSFFont hssfFont = wb.createFont();
        if (font != null) {
            //颜色
            hssfFont.setColor(font.fontColor());
            //设置字体大小
            hssfFont.setFontHeightInPoints(font.fontHeightInPoints());
            //字体
            hssfFont.setFontName(font.fontName());
        } else {
            //颜色
            hssfFont.setColor((short) 0);
            //设置字体大小
            hssfFont.setFontHeightInPoints((short)10);
            //字体
            hssfFont.setFontName("Arial");
        }

        return hssfFont;
    }

    public HSSFCellStyle halign(HSSFWorkbook wb, Style style, HSSFCellStyle cellStyle) {
        if (cellStyle == null) {
            cellStyle = wb.createCellStyle();
        }
        short halign = style.halign();
        if (halign != -1) {
            cellStyle.setAlignment(halign);  // 设置单元格水平方向对其方式
        }
        return cellStyle;
    }

    public HSSFCellStyle valign(HSSFWorkbook wb, Style style, HSSFCellStyle cellStyle) {
        if (cellStyle == null) {
            cellStyle = wb.createCellStyle();
        }
        short valign = style.valign();
        if (valign != -1) {
            cellStyle.setVerticalAlignment(valign); // 设置单元格垂直方向对其方式
        }
        return cellStyle;
    }

    public HSSFCellStyle border(HSSFWorkbook wb, Style style, HSSFCellStyle cellStyle) {
        if (cellStyle == null) {
            cellStyle = wb.createCellStyle();
        }
        short boder = style.border();
        short color = style.color();
        if (boder > 0) {
            cellStyle.setBorderBottom(boder); // 底部边框
            cellStyle.setBorderLeft(boder);  // 左边边框
            cellStyle.setBorderRight(boder); // 右边边框
            cellStyle.setBorderTop(boder); // 上边边框
        }
        if (color >= 8) {
            cellStyle.setBottomBorderColor(color); // 底部边框颜色
            cellStyle.setLeftBorderColor(color); // 左边边框颜色
            cellStyle.setRightBorderColor(color);  // 右边边框颜色
            cellStyle.setTopBorderColor(color);  // 上边边框颜色
        }
        return cellStyle;
    }

    public HSSFCellStyle fillPattern(HSSFWorkbook wb, Style style, HSSFCellStyle cellStyle) {
        if (cellStyle == null) {
            cellStyle = wb.createCellStyle();
        }
        short backgroundColor = style.backgroundColor();
        short fillPattern = style.fillPattern();
        if (backgroundColor >= 8 &&backgroundColor !=9) {
            cellStyle.setFillPattern(CellStyle.SOLID_FOREGROUND);
            cellStyle.setFillForegroundColor(backgroundColor); // 前景色
        }
        if (fillPattern > 0) {
            cellStyle.setFillPattern(fillPattern);
        }
        return cellStyle;
    }


}
